﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;

namespace intTrans
{
    class HarrisDetector
    {
        private Bitmap BitPic;

        public HarrisDetector(string filename)
        {
            BitPic = new Bitmap(filename);
        }

        public Bitmap Transform(ProgressBar progbar)
        {
            int w = 4;
            double k = 0.1;
            progbar.Maximum = 4 * BitPic.Height;
            progbar.Value = 0;
            imageByteConverter ic = new imageByteConverter();
            int[,] kernel = { { 2, 4, 5, 4, 2 }, { 4, 9, 12, 9, 4 }, { 5, 12, 15, 12, 5 }, { 4, 9, 12, 9, 4 }, { 2, 4, 5, 4, 2 } };
            Bitmap gauss = (Bitmap)Convolution(BitPic, kernel,159, progbar);
            int[,] kernelX = { { 1, 0, -1 }, { 2, 0, -2 }, { 1, 0, -1 } };
            int[,] kernelY = { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };
            byte[] MX = ic.ImageToByteArray((Bitmap)Convolution(gauss, kernelX,1, progbar));
            byte[] MY = ic.ImageToByteArray((Bitmap)Convolution(gauss, kernelY,1, progbar));
            byte[] outPic = new byte[MX.Length];
            byte[] NonMax = new byte[MX.Length];
            for (int row = 0; row < BitPic.Height; row++)
            {
                for (int col = 0; col < BitPic.Width; col++)
                {
                    int index = row * ic.stride() + col * ic.bytesPerPixel();
                    NonMax[index] = outPic[index] = (byte)Math.Sqrt(MX[index] * MX[index] + MY[index] * MY[index]);
                    NonMax[index + 1] = outPic[index + 1] = (byte)Math.Sqrt(MX[index + 1] * MX[index + 1] + MY[index + 1] * MY[index + 1]);
                    NonMax[index + 2] = outPic[index + 2] = (byte)Math.Sqrt(MX[index + 2] * MX[index + 2] + MY[index + 2] * MY[index + 2]);
                    if (ic.bytesPerPixel() == 4) NonMax[index + 3] = outPic[index + 3] = MX[index + 3];
                }
                progbar.Value++;
            }
            double Teta;
           
            for (int row = 0; row < BitPic.Height; row++)
            {
                for (int col = 0; col < BitPic.Width; col++)
                {
                    if (row > 1 && row < BitPic.Height - 2 && col > 1 && col < BitPic.Width - 2)
                    {
                        int index = row * ic.stride() + col * ic.bytesPerPixel();
                        if (MY[index] == 0) Teta = 90.0f;
                        else Teta = (double)(Math.Atan(MX[index] / (-1.0*MY[index])) * 180 / Math.PI);

                        for (int i = 1; i <= 2; i++)
                        {
                            //Horizontal Edge
                            if (((-22.5 < Teta) && (Teta <= 22.5)) || ((157.5 < Teta) && (Teta <= -157.5)))
                            {
                                if ((outPic[index] < outPic[index + ic.bytesPerPixel() * i]) || (outPic[index] < outPic[index - ic.bytesPerPixel() * i]))
                                {
                                    NonMax[index] = 0;
                                    NonMax[index + 1] = 0;
                                    NonMax[index + 2] = 0;
                                }
                            }

                            //Vertical Edge
                            if (((-112.5 < Teta) && (Teta <= -67.5)) || ((67.5 < Teta) && (Teta <= 112.5)))
                            {
                                if ((outPic[index] < outPic[index + ic.stride() * i]) || (outPic[index] < outPic[index - ic.stride() * i]))
                                {
                                    NonMax[index] = 0;
                                    NonMax[index + 1] = 0;
                                    NonMax[index + 2] = 0;
                                }
                            }

                            //+45 Degree Edge
                            if (((-67.5 < Teta) && (Teta <= -22.5)) || ((112.5 < Teta) && (Teta <= 157.5)))
                            {
                                if ((outPic[index] < outPic[index + ic.stride() * i - ic.bytesPerPixel() * i]) || (outPic[index] < outPic[index - ic.stride() * i + ic.bytesPerPixel() * i]))
                                {
                                    NonMax[index] = 0;
                                    NonMax[index + 1] = 0;
                                    NonMax[index + 2] = 0;
                                }
                            }

                            //-45 Degree Edge
                            if (((-157.5 < Teta) && (Teta <= -112.5)) || ((67.5 < Teta) && (Teta <= 22.5)))
                            {
                                if ((outPic[index] < outPic[index + ic.stride() * i + ic.bytesPerPixel() * i]) || (outPic[index] < outPic[index - ic.stride() * i - ic.bytesPerPixel() * i]))
                                {
                                    NonMax[index] = 0;
                                    NonMax[index + 1] = 0;
                                    NonMax[index + 2] = 0;
                                }
                            }
                        }
                        if (NonMax[index] < 50)
                        {
                            NonMax[index] = 0;
                            NonMax[index + 1] = 0;
                            NonMax[index + 2] = 0;
                        }
                    }
                }
            }
            for (int x = w + 1; x < BitPic.Height - w; x++) //pixel (x,y)
            {
                for (int y = w + 1; y < BitPic.Width - w; y++)
                {
                    int A, B, C;
                    int index = x * ic.stride() + y * ic.bytesPerPixel();
                    if (NonMax[index] != 0)
                    {
                        //compute window average
                        A = 0; B = 0; C = 0;
                        for (int i = -w; i <= w; i++)
                        {
                            for (int j = -w; j <= w; j++)
                            {
                                A = A + MX[index + ic.stride() * i + ic.bytesPerPixel() * j] * MX[index + ic.stride() * i + ic.bytesPerPixel() * j];
                                B = B + MY[index + ic.stride() * i + ic.bytesPerPixel() * j] * MY[index + ic.stride() * i + ic.bytesPerPixel() * j];
                                C = C + MX[index + ic.stride() * i + ic.bytesPerPixel() * j] * MY[index + ic.stride() * i + ic.bytesPerPixel() * j];
                            }
                        }
                        double var = (A * B - C) * (A * B - C) - k * ((A + B) * (A + B));
                        if (var < 0) var = 0;
                        if (var > 255) var = 255;
                        outPic[index] = (byte)var;
                        outPic[index + 1] = (byte)var;
                        outPic[index + 2] = (byte)var;
                        
                    }
                    else
                    {
                        outPic[index] = 0;
                        outPic[index + 1] = 0;
                        outPic[index + 2] = 0;
                    }
                }
            }
            return (Bitmap)ic.ByteArrayToImage(outPic);
        }

        public Image Convolution(Image img, int[,] kernel, int normalize, ProgressBar pb)
        {
            imageByteConverter ic = new imageByteConverter();
            byte[] inPic = ic.ImageToByteArray((Bitmap)img);
            int kernelSizeX = kernel.GetLength(0);
            int kernelSizeY = kernel.GetLength(1);
            byte[] outPic = new byte[inPic.Length];
            for (int row = 0; row < img.Height; row++)
            {
                for (int col = 0; col < img.Width; col++)
                {
                    int index = row * ic.stride() + col * ic.bytesPerPixel();
                    int sumR = 0;
                    int sumG = 0;
                    int sumB = 0;
                    int avarage = 0;
                    for (int kernelRow = -kernelSizeX / 2; kernelRow <= kernelSizeX / 2; kernelRow++)
                    {
                        for (int kernelCol = -kernelSizeY / 2; kernelCol <= kernelSizeY / 2; kernelCol++)
                        {
                            int kernelIndex, kr, kc;
                            if (row + kernelRow < 0 || row + kernelRow >= img.Height) kr = (row - kernelRow) * ic.stride();
                            else kr = (row + kernelRow) * ic.stride();

                            if (col + kernelCol < 0 || col + kernelCol >= img.Width) kc = (col - kernelCol) * ic.bytesPerPixel();
                            else kc = (col + kernelCol) * ic.bytesPerPixel();

                            kernelIndex = kr + kc;

                            sumR += inPic[kernelIndex + 2] * kernel[kernelRow + kernelSizeX / 2, kernelCol + kernelSizeY / 2];
                            sumG += inPic[kernelIndex + 1] * kernel[kernelRow + kernelSizeX / 2, kernelCol + kernelSizeY / 2];
                            sumB += inPic[kernelIndex] * kernel[kernelRow + kernelSizeX / 2, kernelCol + kernelSizeY / 2];
                        }
                    }

                    avarage = (sumR + sumB + sumG) / 3;
                    avarage = avarage / normalize;
                    if (avarage < 0) avarage = 0;
                    if (avarage > 255) avarage = 255;

                    outPic[index + 2] = (byte)avarage;
                    outPic[index + 1] = (byte)avarage;
                    outPic[index] = (byte)avarage;
                    if (ic.bytesPerPixel() == 4) outPic[index + 3] = inPic[index + 3];

                }
                pb.Value++;
            }
            return ic.ByteArrayToImage(outPic);
        }
    }
}
